﻿
using NetOffice;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using static MainApp.Utils.User32Invoke;

namespace MainApp.Utils
{
    /// <summary>
    /// ppt窗口工具类
    /// </summary>
    static partial class PptWindowUtils
    {
        #region 自定定义
        private static uint objid_NATIVEOM = 0xFFFFFFF0;
        private static Guid _dispatch = new Guid("00020400-0000-0000-C000-000000000046");
        private static Guid _unknown = new Guid("00000000-0000-0000-C000-000000000046");
        private static readonly List<DocumentHwndMapping> DocumentHwndMappings;
        private static readonly object DocumentHwndMappingsSyncRoot;
        #endregion

        #region 构造函数
        static PptWindowUtils()
        {
            DocumentHwndMappings = new List<DocumentHwndMapping>();
            DocumentHwndMappingsSyncRoot = (DocumentHwndMappings as ICollection).SyncRoot;
        }
        #endregion

        #region 公共方法
        /// <summary>
        /// 获取文档窗口对应的句柄
        /// </summary>
        /// <param name="incomeWindowProxy">传入的文档窗口的COM对象</param>
        /// <param name="hwndSourceType">获取到的句柄来源</param>
        /// <param name="canByCursor">标识是否可用通过鼠标所处窗口来进行对比</param>
        /// <returns>返回文档窗口的句柄</returns>
        public static int TryGetHostDocumentWindowHandleFromDesktop(object incomeWindowProxy, out HwndSourceTypes hwndSourceType, bool canByCursor = false, bool ignoreCache = false)
        {
            int result = 0;
            hwndSourceType = HwndSourceTypes.None;
            if (null == incomeWindowProxy)
                throw new ArgumentNullException("windowProxy");
            try
            {
                //文档窗口及其标题
                dynamic documentWindow = incomeWindowProxy;
                string caption = documentWindow.Caption;
                dynamic commandBars = documentWindow.Application.CommandBars;

                #region 从缓存中读取数据
                if (!ignoreCache && TryGetHostDocumentWindowHandleFromCache(incomeWindowProxy, out result) && result > 0)
                {
                    hwndSourceType = HwndSourceTypes.FromCache;
                    return result;
                }
                #endregion

                //根据类名枚举Powerpoint或wps程序窗口
                var enumerator = new WindowEnumerator("PP", "FrameClass");
                IntPtr[] handles = enumerator.EnumerateWindows(2000);
                //if ((handles?.Length ?? 0) == 0)
                //    return result;
                ////如果只有一个句柄则直接返回
                //if (handles.Length == 1)
                //{
                //    result = (int)handles[0];
                //    hwndSourceType = HwndSourceTypes.SingleWindow;
                //    return result;
                //}

                #region 根据句柄对应的文档窗口来与传入的文档窗口进行对比来确定句柄
                foreach (IntPtr hwnd in handles)
                {
                    try
                    {
                        int currentHwnd = (int)hwnd;
                        //检查缓存中是否存在当前句柄
                        if (TryGetDocumentWindowProxyFromCache(currentHwnd, out object cachedProxyObject))
                        {
                            if (cachedProxyObject != incomeWindowProxy)
                                continue;
                            else
                            {
                                result = currentHwnd;
                                hwndSourceType = HwndSourceTypes.FromCache;
                                return result;
                            }
                        }
                        else
                        {
                            //对比句柄对应的COM对象
                            object proxyDocumentWindow = null;
                            try
                            {
                                proxyDocumentWindow = GetAccessibleObject(hwnd, out bool isByPaneClassDC, out bool isByMdiClass);
                                if (null != proxyDocumentWindow)
                                {
                                    dynamic d1 = proxyDocumentWindow;
                                    dynamic d2 = incomeWindowProxy;
                                    bool equals = proxyDocumentWindow == incomeWindowProxy || d1.Presentation.FullName == d2.Presentation.FullName;
                                    if (equals)
                                    {
                                        result = currentHwnd;
                                        if (isByPaneClassDC)
                                            hwndSourceType = HwndSourceTypes.COMObjByPaneClassDCCompare;
                                        else if (isByMdiClass)
                                            hwndSourceType = HwndSourceTypes.COMObjByMdiClassCompare;
                                        break;
                                    }
                                }
                            }
                            finally
                            {
                                if (null != proxyDocumentWindow)
                                    Marshal.ReleaseComObject(proxyDocumentWindow);
                            }
                        }
                    }
                    catch (Exception ex) { }
                }
                #endregion

                #region 如果运行通过鼠标所处窗口来判断则通过此方式进行处理
                if (result <= 0 && canByCursor)
                {
                    Point point = new Point();
                    if (GetCursorPos(ref point))
                    {
                        IntPtr cursorWindowHandle = WindowFromPoint(point);
                        if (cursorWindowHandle != IntPtr.Zero && handles.Contains(cursorWindowHandle))
                        {
                            StringBuilder windowTitle = new StringBuilder(255);
                            GetWindowText(cursorWindowHandle, windowTitle, 255);
                            var title = windowTitle.ToString();
                            if (!string.IsNullOrEmpty(title) && title.StartsWith(caption))
                            {
                                result = (int)cursorWindowHandle;
                                hwndSourceType = HwndSourceTypes.CursorHwndCompare;
                            }
                        }
                    }
                }
                #endregion

                #region 如果没有找到合适句柄则采用匹配文档窗口的标题的模式来处理
                if (result <= 0)
                {
                    foreach (IntPtr hwnd in handles)
                    {
                        StringBuilder windowTitle = new StringBuilder(255);
                        GetWindowText(hwnd, windowTitle, 255);
                        var title = windowTitle.ToString();
                        if (!string.IsNullOrEmpty(title) && title.StartsWith(caption))
                        {
                            result = (int)hwnd;
                            hwndSourceType = HwndSourceTypes.CaptionCompare;
                        }
                    }
                }
                #endregion
            }
            catch (Exception exception)
            {
                throw exception;
            }
            finally
            {
                //添加到缓存
                if (result > 0 && hwndSourceType != HwndSourceTypes.FromCache && !ignoreCache)
                    AddDocumentWindowHandleToCache(incomeWindowProxy, result);
            }
            return result;
        }
        /// <summary>
        /// 获取指定文档窗口的句柄
        /// </summary>
        /// <param name="window">文档窗口</param>
        /// <param name="hwndSourceType">获取到的句柄来源</param>
        /// <param name="canByCursor">标识是否可用通过鼠标所处窗口来进行对比</param>
        /// <param name="ignoreCache">标识是否忽略缓存</param>
        /// <returns></returns>
        public static int GetHwnd(NetOffice.PowerPointApi.DocumentWindow window, out HwndSourceTypes hwndSourceType, bool canByCursor = false, bool ignoreCache = false)
        {
            var hwnd = TryGetHostDocumentWindowHandleFromDesktop(window.UnderlyingObject, out hwndSourceType, canByCursor: canByCursor, ignoreCache: ignoreCache);
            return hwnd;
        }
        /// <summary>
        /// 移除指定文档窗口的缓存
        /// </summary>
        /// <param name="window"></param>
        public static void RemoveWindowCache(NetOffice.PowerPointApi.DocumentWindow window)
        {
            if (window == null)
                return;
            object windowProxy = null;
            try { windowProxy = window.UnderlyingObject; } catch { }
            if (windowProxy == null)
                return;
            lock (DocumentHwndMappingsSyncRoot)
            {
                var mapping = DocumentHwndMappings.FirstOrDefault(p => p.DocumentWindowProxy == windowProxy);
                if (mapping != null)
                    DocumentHwndMappings.Remove(mapping);
            }
        }
        /// <summary>
        /// 检查窗口对应的com对象是否为指定文档
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="presentation"></param>
        /// <returns></returns>
        public static bool CheckAccessibleObjectEqules(IntPtr hwnd, NetOffice.PowerPointApi.Presentation presentation)
        {
            if (hwnd == IntPtr.Zero || presentation == null)
                return false;
            object documentProxy = null;
            try { documentProxy = presentation.UnderlyingObject; } catch { }
            if (documentProxy == null)
                return false;
            var handled = false;
            object accessibleObject = null;
            var className = User32Wrapper.GetHwndClassName(hwnd);
            //检查活动窗口是否为PP**FrameClass窗口
            if (className.StartsWith("PP", StringComparison.CurrentCultureIgnoreCase) &&
                className.EndsWith("FrameClass", StringComparison.CurrentCultureIgnoreCase))
            {
                handled = true;
                accessibleObject = GetAccessibleObject(hwnd, out bool _, out bool _);
            }
            //检查是否为mdiClass或paneClassDC窗口
            if (className.Equals("mdiClass", StringComparison.CurrentCultureIgnoreCase) ||
                className.Equals("paneClassDC", StringComparison.CurrentCultureIgnoreCase))
            {
                handled = true;
                accessibleObject = GetAccessibleObject(hwnd);
            }
            if (!handled)
            {
                accessibleObject = GetAccessibleObject(hwnd, out bool _, out bool _);
            }
            object documentObject = null;
            if (accessibleObject != null)
            {
                try { documentObject = Core.Default.Invoker.PropertyGet(accessibleObject, "Presentation"); }
                catch { }
                finally { Marshal.ReleaseComObject(accessibleObject); }
                accessibleObject = documentObject;
            }
            var equles = accessibleObject == documentProxy;
            if (accessibleObject != null)
                Marshal.ReleaseComObject(accessibleObject);
            return equles;
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 添加文档窗口-句柄映射信息缓存
        /// </summary>
        /// <param name="documentWindowProxy"></param>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        private static bool AddDocumentWindowHandleToCache(object documentWindowProxy, int hwnd)
        {
            bool result = false;
            if (hwnd > 0)
            {
                try
                {
                    lock (DocumentHwndMappingsSyncRoot)
                    {
                        DocumentHwndMappings.Add(new DocumentHwndMapping(documentWindowProxy, hwnd));
                        result = true;
                    }
                }
                catch { }
            }
            return result;
        }

        /// <summary>
        /// 移除文档窗口-句柄映射信息缓存
        /// </summary>
        /// <param name="documentWindowProxy"></param>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        private static bool RemoveDocumentWindowHandleFromCache(object documentWindowProxy, IntPtr hwnd)
        {
            bool result = false;
            try
            {
                lock (DocumentHwndMappingsSyncRoot)
                {
                    var mapping = DocumentHwndMappings.FirstOrDefault(p => p.DocumentWindowProxy == documentWindowProxy);
                    if (mapping != null)
                        result = DocumentHwndMappings.Remove(mapping);
                }
            }
            catch { }
            return result;
        }

        /// <summary>
        /// 从文档窗口-句柄映射信息缓存中查找指定文档窗口的句柄
        /// </summary>
        /// <param name="documentWindowProxy"></param>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        private static bool TryGetHostDocumentWindowHandleFromCache(object documentWindowProxy, out int hwnd)
        {
            bool success = false;
            hwnd = 0;
            try
            {
                lock (DocumentHwndMappingsSyncRoot)
                {
                    try
                    {
                        var mapping = DocumentHwndMappings.FirstOrDefault(p => p.DocumentWindowProxy == documentWindowProxy);
                        if (mapping != null)
                        {
                            success = true;
                            hwnd = mapping.Hwnd;
                        }
                    }
                    finally
                    {
                        for (var i = DocumentHwndMappings.Count - 1; i >= 0; i--)
                        {
                            object presentation = null;
                            var mapping = DocumentHwndMappings[i];
                            dynamic documentWindow = mapping.DocumentWindowProxy;
                            try { presentation = documentWindow.Presentation; }
                            catch (Exception ex) { }
                            if (presentation == null)
                                DocumentHwndMappings.Remove(mapping);
                        }
                    }
                }
            }
            catch { }
            return success;
        }

        /// <summary>
        /// 从文档窗口-句柄映射信息缓存中查找指定句柄的文档窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="proxyObject"></param>
        /// <returns></returns>
        private static bool TryGetDocumentWindowProxyFromCache(int hwnd, out object proxyObject)
        {
            bool success = false;
            proxyObject = null;
            try
            {
                lock (DocumentHwndMappingsSyncRoot)
                {
                    var mapping = DocumentHwndMappings.FirstOrDefault(p => p.Hwnd == hwnd);
                    if (mapping != null)
                    {
                        success = true;
                        proxyObject = mapping.DocumentWindowProxy;
                    }
                }
            }
            catch { }
            return success;
        }

        /// <summary>
        /// 枚举指定句柄窗口的类名为paneClassDC的子窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private static bool EnumChildProcByPaneClassDC(IntPtr hwnd, IntPtr lParam)
        {
            GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);
            if (gcChildhandlesList.Target == null)
                return false;
            var windowClass = new StringBuilder(128);
            GetClassName(hwnd, windowClass, windowClass.Capacity);
            var className = windowClass.ToString();
            if (className == "paneClassDC")
            {
                List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
                childHandles.Add(hwnd);
                //停止枚举，继续枚举对于wps多个标签将导致取出来的是非激活的标签句柄
                return false;
            }
            return true;
        }

        /// <summary>
        /// 枚举指定句柄窗口的类名为mdiClass的子窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private static bool EnumChildProcByMdiClass(IntPtr hwnd, IntPtr lParam)
        {
            GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);
            if (gcChildhandlesList.Target == null)
                return false;
            var windowClass = new StringBuilder(128);
            GetClassName(hwnd, windowClass, windowClass.Capacity);
            var className = windowClass.ToString();
            if (className == "mdiClass")
            {
                //停止枚举，继续枚举对于wps多个标签将导致取出来的是非激活的标签句柄
                List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
                childHandles.Add(hwnd);
                return false;
            }
            return true;
        }
        /// <summary>
        /// 获取类名为paneClassDC的子窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        private static IntPtr GetChildHwndByPaneClassDC(IntPtr hwnd)
        {
            List<IntPtr> childHandles = new List<IntPtr>();
            GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles);
            IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
            try
            {
                EnumWindowProc cb = new EnumWindowProc(EnumChildProcByPaneClassDC);
                EnumChildWindows(hwnd, cb, pointerChildHandlesList);
            }
            finally
            {
                gcChildhandlesList.Free();
            }
            if (childHandles.Count > 0)
                return childHandles.FirstOrDefault();
            return IntPtr.Zero;
        }
        /// <summary>
        /// 获取类名为mdiClass的子窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        private static IntPtr GetChildHwndByMdiClass(IntPtr hwnd)
        {
            List<IntPtr> childHandles = new List<IntPtr>();
            GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles);
            IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
            try
            {
                EnumWindowProc cb = new EnumWindowProc(EnumChildProcByMdiClass);
                EnumChildWindows(hwnd, cb, pointerChildHandlesList);
            }
            finally
            {
                gcChildhandlesList.Free();
            }
            if (childHandles.Count > 0)
                return childHandles.FirstOrDefault();
            return IntPtr.Zero;
        }
        /// <summary>
        /// 获取指定窗口句柄当前的活动文档窗口COM对象
        /// </summary>
        /// <remarks>通过枚举指定句柄下面的“mdiClass”或“paneClassDC”类窗口，并获取枚举到的“mdiClass”或“paneClassDC”类窗口对应的COM对象</remarks>
        /// <param name="hwnd"></param>
        /// <param name="isByPaneClassDC"></param>
        /// <param name="isByMdiClass"></param>
        /// <returns></returns>
        private static object GetAccessibleObject(IntPtr hwnd, out bool isByPaneClassDC, out bool isByMdiClass)
        {
            isByMdiClass = false;
            isByPaneClassDC = false;
            if (hwnd == IntPtr.Zero)
                return null;
            //查找类名为paneClassDC的窗口
            var hWndChild = GetChildHwndByPaneClassDC(hwnd);
            isByPaneClassDC = hWndChild != IntPtr.Zero;
            if (hWndChild == IntPtr.Zero)
            {
                hWndChild = GetChildHwndByMdiClass(hwnd);
                isByMdiClass = hWndChild != IntPtr.Zero;
            }
            if (hWndChild == IntPtr.Zero)
                return null;
            //查找窗口对应的COM对象
            object ptr = null;
            int hr = AccessibleObjectFromWindow(hWndChild, objid_NATIVEOM, _dispatch.ToByteArray(), ref ptr);
            if (hr >= 0)
                return ptr;
            return null;
        }
        /// <summary>
        /// 获取指定窗口句柄当前的活动文档窗口COM对象
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        private static object GetAccessibleObject(IntPtr hWnd)
        {
            object ptr = null;
            int hr = AccessibleObjectFromWindow(hWnd, objid_NATIVEOM, _dispatch.ToByteArray(), ref ptr);
            if (hr >= 0)
                return ptr;
            return null;
        }

        #endregion

        #region 类型定义
        class DocumentHwndMapping
        {
            /// <summary>
            /// 获取文档窗口COM对象
            /// </summary>
            public object DocumentWindowProxy { get; }
            /// <summary>
            /// 获取文档窗口句柄
            /// </summary>
            public int Hwnd { get; }
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="documentWindowProxy"></param>
            /// <param name="hwnd"></param>
            internal DocumentHwndMapping(object documentWindowProxy, int hwnd)
            {
                Hwnd = hwnd;
                DocumentWindowProxy = documentWindowProxy;
            }
        }
        class DocumentWindowEqualityComparer : IEqualityComparer<NetOffice.PowerPointApi.DocumentWindow>
        {
            public bool Equals(NetOffice.PowerPointApi.DocumentWindow x, NetOffice.PowerPointApi.DocumentWindow y)
            {
                var eq = x == y;
                return eq;
            }

            public int GetHashCode(NetOffice.PowerPointApi.DocumentWindow obj)
            {
                return $"{obj.Caption}".GetHashCode();
            }
        }
        /// <summary>
        /// Enumerate Top Level Windows on Desktop
        /// </summary>
        public class WindowEnumerator
        {
            #region Embedded Types

            /// <summary>
            /// Internal operation mode 
            /// </summary>
            public enum FilterMode
            {
                /// <summary>
                ///  Class name must match totally
                /// </summary>
                Full = 0,

                /// <summary>
                /// Class name must match in start
                /// </summary>
                Start = 2,

                /// <summary>
                /// Class name must match in end
                /// </summary>
                End = 3,

                /// <summary>
                /// Class name must match in start and end of name
                /// </summary>
                StartEnd = 1,
            }

            #endregion

            #region Fields

            private static object _lockInstance = new object();
            private static WindowEnumerator _currentInstance;

            #endregion

            #region Ctor

            /// <summary>
            /// Creates an instance of the class
            /// </summary>
            /// <param name="filter">optional class name filter or null</param>
            public WindowEnumerator(string filter)
                : this(filter, filter, FilterMode.Full)
            {
            }

            /// <summary>
            /// Creates an instance of the class
            /// </summary>
            /// <param name="filter">optional class name filter or null</param>
            /// <param name="mode">current filter mode</param>
            public WindowEnumerator(string filter, FilterMode mode)
                : this(filter, filter, mode)
            {
            }

            /// <summary>
            /// Creates an instance of the class
            /// </summary>
            /// <param name="startsWithfilter">starts with class name filter</param>
            /// <param name="endsWithFilter">ends with class name filter</param>
            public WindowEnumerator(string startsWithfilter, string endsWithFilter)
                : this(startsWithfilter, endsWithFilter, FilterMode.StartEnd)
            {
            }

            /// <summary>
            /// Creates an instance of the class
            /// </summary>
            /// <param name="handle">target window handle</param>
            /// <param name="startsWithfilter">starts with class name filter</param>
            /// <param name="endsWithFilter">ends with class name filter</param>
            /// <param name="mode">current filter mode</param>
            public WindowEnumerator(string startsWithfilter, string endsWithFilter, FilterMode mode)
            {
                Mode = mode;
                Filter = startsWithfilter;
                StartsWithFilter = startsWithfilter;
                EndsWithFilter = endsWithFilter;
            }
            #endregion

            #region Properties

            /// <summary>
            /// Optional class name filter or null
            /// </summary>
            public string Filter { get; private set; }

            /// <summary>
            /// Class name begin
            /// </summary>
            public string StartsWithFilter { get; private set; }

            /// <summary>
            /// Class name end
            /// </summary>
            public string EndsWithFilter { get; private set; }

            /// <summary>
            /// Current Filter Mode
            /// </summary>
            public FilterMode Mode { get; private set; }

            #endregion

            #region Methods

            /// <summary>
            /// Encapsulate WindowsAPI method GetWindowRect
            /// </summary>
            /// <param name="hwnd">target window handle</param>
            /// <returns>window coordinates dimensions to screen</returns>
            public static Rectangle GetWindowRect(IntPtr hwnd)
            {
                User32Invoke.RECT rect = new User32Invoke.RECT();
                User32Invoke.GetWindowRect(hwnd, out rect);
                return new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
            }

            class EnumerateThreadPara
            {
                public ManualResetEvent ManualResetEvent { get; }
                public List<IntPtr> Results { get; }
                public EnumerateThreadPara()
                {
                    Results = new List<IntPtr>();
                    ManualResetEvent = new ManualResetEvent(false);
                }
            }

            /// <summary>
            /// Enumerates all top level windows on desktop. WARNING: The method returns null if operation timeout is reached.
            /// </summary>
            /// <param name="millisecondsTimeout">a timeout for the operation. when a desktop windows is busy or non responding these method freeze. you can handle this with the operation timeout</param>
            /// <returns>result array or null</returns>
            public IntPtr[] EnumerateWindows(int millisecondsTimeout)
            {
                if (millisecondsTimeout < 0)
                    throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));

                try
                {
                    lock (_lockInstance)
                    {
                        _currentInstance = this;
                        var para = new EnumerateThreadPara();
                        Thread thread = new Thread(new ParameterizedThreadStart(EnumerateWindowsAsync)) { IsBackground = true };
                        WaitHandle[] waitHandles = new WaitHandle[1];
                        waitHandles[0] = para.ManualResetEvent;
                        thread.Start(para);
                        bool result = WaitHandle.WaitAll(waitHandles, millisecondsTimeout);
                        if (!result)
                        {
                            thread.Abort();
                            para.Results.Clear();
                            _currentInstance = null;
                            return null;
                        }
                        else
                        {
                            _currentInstance = null;
                        }
                        return para.Results.ToArray();
                    }
                }
                catch (Exception exception)
                {
                    DebugConsole.Default.WriteException(exception);
                    throw;
                }
            }

            /// <summary>
            /// Returns information a window is currently visible
            /// </summary>
            /// <param name="handle">target window handle</param>
            /// <returns>true if window is visible, otherwise false</returns>
            public bool IsVisible(IntPtr handle)
            {
                if (IntPtr.Zero == handle)
                    throw new ArgumentNullException("handle");
                return User32Invoke.IsWindowVisible(handle);
            }

            private void EnumerateWindowsAsync(object paraObject)
            {
                GCHandle gcHandlesList = default;
                try
                {
                    var para = paraObject as EnumerateThreadPara;
                    var manualReset = para.ManualResetEvent;
                    var handles = para.Results;
                    gcHandlesList = GCHandle.Alloc(handles);
                    IntPtr pointerHandlesList = GCHandle.ToIntPtr(gcHandlesList);
                    User32Invoke.EnumWindows(new User32Invoke.EnumWindowsProc(EnumTopLevelWindows), pointerHandlesList);
                    manualReset?.Set();
                }
                catch (Exception exception)
                {
                    DebugConsole.Default.WriteException(exception);
                }
                finally
                {
                    if (gcHandlesList.IsAllocated)
                        gcHandlesList.Free();
                }
            }

            private static bool EnumTopLevelWindows(IntPtr hWnd, IntPtr lParam)
            {
                try
                {
                    GCHandle gcHandlesList = GCHandle.FromIntPtr(lParam);
                    if (gcHandlesList.Target == null)
                        return false;
                    int size = User32Invoke.GetWindowTextLength(hWnd);
                    if (size++ > 0)
                    {
                        StringBuilder sb = new StringBuilder(size);
                        int nRet;
                        StringBuilder sb2 = new StringBuilder(100);
                        nRet = User32Invoke.GetClassName(hWnd, sb2, sb2.Capacity);
                        if (nRet != 0)
                        {
                            string className = sb2.ToString();
                            if (FilterMatch(className, _currentInstance))
                            {
                                List<IntPtr> handles = gcHandlesList.Target as List<IntPtr>;
                                handles.Add(hWnd);
                            }
                        }
                    }
                    return true;
                }
                catch (Exception exception)
                {
                    DebugConsole.Default.WriteException(exception);
                    return true;
                }
            }

            private static bool FilterMatch(string className, WindowEnumerator instance)
            {
                switch (instance.Mode)
                {
                    case FilterMode.Full:
                        {
                            var filter = _currentInstance.Filter;
                            if (string.IsNullOrWhiteSpace(filter))
                                return true;
                            return filter.Equals(className, StringComparison.InvariantCultureIgnoreCase);
                        }
                    case FilterMode.Start:
                        {
                            string start = !string.IsNullOrWhiteSpace(instance.StartsWithFilter) ? instance.StartsWithFilter.ToLower() : string.Empty;
                            string target = className.ToLower();
                            if (!string.IsNullOrWhiteSpace(start))
                                return target.StartsWith(start);
                            return false;
                        }
                    case FilterMode.End:
                        {
                            string end = !string.IsNullOrWhiteSpace(instance.EndsWithFilter) ? instance.EndsWithFilter.ToLower() : string.Empty;
                            string target = className.ToLower();
                            if (!string.IsNullOrWhiteSpace(end))
                                return target.EndsWith(end);
                            return false;
                        }
                    case FilterMode.StartEnd:
                        {
                            string start = !string.IsNullOrWhiteSpace(instance.StartsWithFilter) ? instance.StartsWithFilter.ToLower() : string.Empty;
                            string end = !string.IsNullOrWhiteSpace(instance.EndsWithFilter) ? instance.EndsWithFilter.ToLower() : string.Empty;
                            if (string.IsNullOrWhiteSpace(start) && string.IsNullOrWhiteSpace(end))
                                return true;
                            string target = className.ToLower();
                            if (!string.IsNullOrWhiteSpace(start) && !string.IsNullOrWhiteSpace(end))
                                return target.StartsWith(start) && target.EndsWith(end);
                            if (!string.IsNullOrWhiteSpace(start))
                                return target.StartsWith(start);
                            if (!string.IsNullOrWhiteSpace(end))
                                return target.EndsWith(end);
                            return false;
                        }
                    default:
                        {
                            throw new IndexOutOfRangeException();
                        }
                }
            }

            #endregion
        }
        #endregion
    }

    enum HwndSourceTypes
    {
        /// <summary>
        /// 无
        /// </summary>
        None,
        /// <summary>
        /// 缓存
        /// </summary>
        FromCache,
        /// <summary>
        /// 单个app窗口
        /// </summary>
        SingleWindow,
        /// <summary>
        /// 文档窗口COM对象对比(paneClassDC类型查找)
        /// </summary>
        COMObjByPaneClassDCCompare,
        /// <summary>
        /// 文档窗口COM对象对比(mdiClass类型查找)
        /// </summary>
        COMObjByMdiClassCompare,
        /// <summary>
        /// 鼠标所在窗口对比
        /// </summary>
        CursorHwndCompare,
        /// <summary>
        /// 文档窗口标题对比
        /// </summary>
        CaptionCompare
    }

    class NativeMethods
    {
        /// <summary>
        /// 获取当前鼠标所处窗口的句柄
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(Point point);
        /// <summary>
        /// 获取指定窗口句柄对应的对象
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="dwObjectID"></param>
        /// <param name="riid"></param>
        /// <param name="ptr"></param>
        /// <returns></returns>
        [DllImport("Oleacc.dll")]
        public static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, [MarshalAs(UnmanagedType.IDispatch)] ref object ptr);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pacc"></param>
        /// <param name="phwnd"></param>
        /// <returns></returns>
        [DllImport("oleacc.dll")]
        public static extern uint WindowFromAccessibleObject([MarshalAs(UnmanagedType.IDispatch)] object pacc, ref IntPtr phwnd);
    }

    /// <summary>
    /// User32.dll封装定义
    /// </summary>
    public class User32Invoke
    {
        private const string USER32 = "USER32.DLL";
        /// <summary>
        /// 检索附加到调用线程的消息队列的活动窗口的窗口句柄。
        /// </summary>
        /// <returns>返回值是附加到调用线程的消息队列的活动窗口的句柄。</returns>
        [DllImport(USER32, SetLastError = true)]
        public static extern IntPtr GetActiveWindow();

        #region GetGUIThreadInfo
        /// <summary>
        /// 若要为另一个线程获取消息队列中活动窗口的窗口句柄
        /// </summary>
        /// <param name="idThread"></param>
        /// <param name="lpgui"></param>
        /// <returns></returns>
        [DllImport(USER32, SetLastError = true)]
        public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
        [Flags]
        public enum GuiThreadInfoFlags
        {
            GUI_CARETBLINKING = 0x00000001,
            GUI_INMENUMODE = 0x00000004,
            GUI_INMOVESIZE = 0x00000002,
            GUI_POPUPMENUMODE = 0x00000010,
            GUI_SYSTEMMENUMODE = 0x00000008
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GUITHREADINFO
        {
            public int cbSize;
            public GuiThreadInfoFlags flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public System.Drawing.Rectangle rcCaret;
        }
        #endregion

        /// <summary>
        /// Determines whether the specified window handle identifies an existing window.
        /// </summary>
        /// <param name="hWnd">A handle to the window to be tested.</param>
        /// <returns>If the window handle identifies an existing window, the return value is true, otherwise it is false.</returns>
        /// <remarks>
        /// A thread should not use IsWindow for a window that it did not create because the window could be destroyed after this function was called.
        /// Further, because window handles are recycled the handle could even point to a different window.
        /// </remarks>
        [DllImport(USER32, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsWindow(IntPtr hWnd);
        /// <summary>
        /// Retrieves a handle to the foreground window (the window with which the user is currently
        /// working). The system assigns a slightly higher priority to the thread that creates the
        /// foreground window than it does to other threads.
        /// <para>
        /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633505%28v=vs.85%29.aspx
        /// for more information.
        /// </para>
        /// </summary>
        /// <returns>
        /// C++ ( Type: Type: HWND ) <br/> The return value is a handle to the foreground window. The
        /// foreground window can be NULL in certain circumstances, such as when a window is losing activation.
        /// </returns>
        [DllImport(USER32, SetLastError = true)]
        public static extern IntPtr GetForegroundWindow();
        /// <summary>
        /// Retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search. To search child windows, beginning with a specified child window, use the FindWindowEx function.
        /// </summary>
        /// <param name="lpClassName">The window class name. If lpClassName is NULL, it finds any window whose title matches the lpWindowName parameter.</param>
        /// <param name="lpWindowName">The window name (the window's title). If this parameter is NULL, all window names match.</param>
        /// <returns>If the function succeeds, the return value is a handle to the window that has the specified
        ///  class name and window name. If the function fails, the return value is NULL.</returns>
        [DllImport(USER32, SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport(USER32, SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr FindWindowEx(
            IntPtr parentHandle,
            IntPtr childAfter,
            string className,
            string windowTitle);
        /// <summary>
        /// Retrieves a handle to the desktop window. The desktop window covers the entire screen. The desktop window is the area on top of which other windows are painted.
        /// </summary>
        /// <returns>The return value is a handle to the desktop window.</returns>
        [DllImport(USER32)]
        public static extern IntPtr GetDesktopWindow();

        /// <summary>
        /// Retrieves a handle to the Shell's desktop window.
        /// </summary>
        /// <returns>The return value is the handle of the Shell's desktop window. If no Shell process is present, the return value is NULL.</returns>
        [DllImport(USER32)]
        public static extern IntPtr GetShellWindow();

        /// <summary>
        ///     Brings the thread that created the specified window into the foreground and activates the window. Keyboard
        ///     input is directed to the window, and various visual cues are changed for the user. The system assigns a slightly
        ///     higher priority to the thread that created the foreground window than it does to other threads.
        /// </summary>
        /// <param name="hWnd">A handle to the window that should be activated and brought to the foreground.</param>
        /// <returns>
        ///     If the window was brought to the foreground, the return value is true.
        ///     <para>If the window was not brought to the foreground, the return value is false.</para>
        /// </returns>
        [DllImport(USER32, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary>
        /// 切换指定窗口到前台
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="fAltTab"></param>
        /// <returns></returns>
        [DllImport(USER32, CharSet = CharSet.Auto)]
        public static extern bool SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport(USER32, SetLastError = true)]
        public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

        /// <summary>
        /// 获取指定窗口句柄对应窗口的类名
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lpClassName"></param>
        /// <param name="nMaxCount"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        /// <summary>
        /// 检索指定窗口的标题栏文本的长度
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport(USER32, CharSet = CharSet.Unicode)]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        /// <summary>
        /// 获取指定窗口句柄对应窗口的标题
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lpString"></param>
        /// <param name="nMaxCount"></param>
        /// <returns></returns>
        [DllImport(USER32, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);

        /// <summary>
        /// 检索创建指定窗口的线程的标识符，以及创建该窗口的进程（可选）的标识符
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="lpdwProcessId">进程id</param>
        /// <returns>返回线程id</returns>
        [DllImport(USER32, SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        /// <summary>
        /// 获取当前鼠标的位置
        /// </summary>
        /// <param name="lpPoint"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        public static extern bool GetCursorPos(ref Point lpPoint);

        /// <summary>
        /// 获取当前鼠标所处窗口的句柄
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        public static extern IntPtr WindowFromPoint(Point point);

        /// <summary>
        /// 获取指定窗口句柄对应窗口是否可见
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        /// <summary>
        /// 获取指定窗口句柄对应窗口是否处于最小化状态
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsIconic(IntPtr hWnd);

        /// <summary>
        /// 获取指定句柄窗口是否最大化
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsZoomed(IntPtr hWnd);

        /// <summary>
        /// 枚举屏幕上的所有顶级窗口
        /// </summary>
        /// <param name="enumProc"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
        public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        /// <summary>
        /// 枚举指定窗口句柄的子窗口
        /// </summary>
        /// <param name="hWndParent"></param>
        /// <param name="lpEnumFunc"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport(USER32)]
        public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);
        /// <summary>
        /// 枚举到子窗口处理
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);

        /// <summary>
        /// 获取指定窗口句柄对应的对象
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="dwObjectID"></param>
        /// <param name="riid"></param>
        /// <param name="ptr"></param>
        /// <returns></returns>
        [DllImport("Oleacc.dll")]
        public static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint dwObjectID, byte[] riid, [MarshalAs(UnmanagedType.IDispatch)] ref object ptr);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pacc"></param>
        /// <param name="phwnd"></param>
        /// <returns></returns>
        [DllImport("oleacc.dll")]
        public static extern int WindowFromAccessibleObject([MarshalAs(UnmanagedType.IDispatch)] object pacc, out IntPtr phwnd);
    }

    /// <summary>
    /// User32.dll高级封装库
    /// </summary>
    public class User32Wrapper
    {
        /// <summary>
        /// 获取指定窗口的类名称
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static string GetHwndClassName(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
                return string.Empty;
            var classBuilder = new StringBuilder(255);
            User32Invoke.GetClassName(hWnd, classBuilder, classBuilder.Capacity);
            return classBuilder.ToString();
        }

        /// <summary>
        /// 获取指定窗口的标题
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static string GetHwndCaption(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
                return string.Empty;
            var captionBuilder = new StringBuilder(255);
            User32Invoke.GetWindowText(hWnd, captionBuilder, captionBuilder.Capacity);
            return captionBuilder.ToString();
        }
    }


}
